#define _CRT_SECURE_NO_WARNINGS
#include<assert.h>
#include<stdio.h>
#include<string.h>
//第17讲：C语⾔内存函数+第18讲：数据在内存中的存储
//1. memcpy 使⽤和模拟实现
//void * memcpy ( void * destination, const void * source, size_t num );	
//• 函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置。
//• 这个函数在遇到 '\0' 的时候并不会停下来。
//• 如果source和destination有任何的重叠，复制的结果都是未定义的。
// num指的是要拷贝空间的大小，而不是元素的个数,num的单位是字节
// 拷贝结束后会返回目标空间的起始地址
//int main()
//{
//	//memcpy - 针对内存块进行拷贝
//	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
//	int arr2[20] = { 0 };
//	memcpy(arr2, arr1, 20);
//	for (int i = 0; i < 20; i++)
//		printf("%d ", arr2[i]);
//	return 0;
//}

//memcpy的模拟实现:
//void* my_memcpy(void* dest, const void* src, size_t num)
//{
//	assert(dest && src);
//	void* ret = dest;
//	while (num--)
//	{
//		*(char*)dest = *(char*)src;
//		dest = (char*)dest + 1;
//		src = (char*)src + 1;
//		//也可以这样写:
//		//((char*)dest)++;
//		//((char*)src)++;
//
//		//但是为什么不能这样写:(char*)dest++;原因是因为后置++是先计算表达式在计算结果的
//		//					 表达式为:(char*)dest,已经强制类型转换过一次了，再++的时候dest已经不是char*类型了
//	}
//	return ret;
//}
//int main()
//{
//	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
//	int arr2[20] = { 0 };
//	//my_memcpy(arr2, arr1, 20);
//	//加入source和destination有重叠呢？
//	my_memcpy(arr1+2, arr1, 20);
//	for (int i = 0; i < sizeof(arr1)/sizeof(arr1[0]); i++)
//		printf("%d ", arr1[i]);
//	return 0;
//}
//不过库里面的memcpy是可以进行重叠拷贝的操作的,但是原本是不希望这样的操作的。
//memcpy完成的功能只需要不重叠拷贝就可以了，不需要具有重叠拷贝的功能,超出了预期


//对于重叠的内存，交给memmove来处理。
//2. memmove 使⽤和模拟实现
//void * memmove ( void * destination, const void * source, size_t num );
//• 和memcpy的差别就是memmove函数处理的源内存块和⽬标内存块是可以重叠的。
//• 如果源空间和⽬标空间出现重叠，就得使⽤memmove函数处理。
//void* my_memmove(void* dest, const void* src, size_t num)
//{
//	void* ret = dest;
//	if (dest < src)
//	{
//		//从前向后
//		while (num--)
//		{
//			*(char*)dest = *(char*)src;
//			((char*)dest)++;
//			((char*)src)++;
//		}
//	}
//	else
//	{
//		//从后往前
//		while (num--)
//			*((char*)dest + num) = *((char*)src + num);
//	}
//	return ret;
//}
//int main()
//{
//	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
//	memmove(arr + 2, arr, 5 * sizeof(int));
//	for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
//		printf("%d ", arr[i]);
//	return 0;
//}


//3. memset 函数的使⽤
//void * memset ( void * ptr, int value, size_t num );
//ptr-->被指向的空间
//value-->指向空间的值都要被修改为value
//num-->指向空间的多少个字节要被改成value
//int main()
//{
//	char str[] = "abcdefg";
//	memset(str, 'x', 3);
//	printf("%s\n",str);
//	
//	int arr[5] = { 0 };
//	memset(arr, 1, 5 * sizeof(int));//是以字节为单位设置的，int中的每一个字节都被设置为01,即:01 01 01 01是一个很大的数字
//	for (int i = 0; i < 5; i++)
//		printf("%d ", arr[i]);
//	return 0;
//}


//4. memcmp 函数的使⽤
//int memcmp ( const void * ptr1, const void * ptr2, size_t num );
//• ⽐较从ptr1和ptr2指针指向的位置开始，向后的num个字节
//如果ptr1指向的内容比ptr2指向的内容大，那就返回大于零的值，反之返回小于0的值，如果相等就返回0，和strcmp很相似
//int main()
//{
//	int arr1[] = { 1,2,3,4,5,6,7 };
//	int arr2[] = { 1,2,3,4,10,2 };
//	printf("%d", memcmp(arr1, arr2, 21));//仍然是一个字节一个字节比较，当比较到第21个字节的时候
//	return 0;							 //arr1里是05 arr2里是0A,没有把第五个元素访问完
//}



//第18讲：数据在内存中的存储
//1. 整数在内存中的存储
//整数的2进制表⽰⽅法有三种，即 原码、反码和补码
//有符号的整数，三种表⽰⽅法均有符号位和数值位两部分，符号位都是⽤0表⽰“正”，⽤1表
//⽰“负”，最⾼位的⼀位是被当做符号位，剩余的都是数值位。

//正整数的原、反、补码都相同。
//负整数的三种表⽰⽅法各不相同

//原码：直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
//反码：将原码的符号位不变，其他位依次按位取反就可以得到反码。
//补码：反码 + 1就得到补码。
//对于整形来说：数据存放内存中其实存放的是补码。
//原因在于，使⽤补码，可以将符号位和数值域统⼀处理；
//int main()
//{
//	int n = 0x11223344;
//	//1.整数在内存中存储的是二进制的补码
//	//2.在调试窗口中观察内存的时候，为了方便展示，显示的是十六进制
//	//3.存储的顺序是倒过来的
//	return 0;
//}


// 2. ⼤⼩端字节序和字节序判断
//如果存储: 0x11223344
//在调试的时候就会看到这个数据是以字节为单位倒着存储的
//2.1 什么是⼤⼩端？
//其实超过⼀个字节的数据在内存中存储的时候，就有存储顺序的问题，按照不同的存储顺序，我们分
//为⼤端字节序存储和⼩端字节序存储，下⾯是具体的概念：
//⼤端（存储）模式：
//是指数据的低位字节内容保存在内存的⾼地址处，⽽数据的⾼位字节内容，保存在内存的低地址处。
//
//⼩端（存储）模式：
//是指数据的低位字节内容保存在内存的低地址处，⽽数据的⾼位字节内容，保存在内存的⾼地址处。

//2.2 为什么有⼤⼩端 ?
//为什么会有⼤⼩端模式之分呢？
//这是因为在计算机系统中，我们是以字节为单位的，每个地址单元都对应着⼀个字节，⼀个字节为8
//bit 位，但是在C语⾔中除了8 bit 的 char 之外，还有16 bit 的 short 型，32 bit 的 long 型（要看
//具体的编译器），另外，对于位数⼤于8位的处理器，例如16位或者32位的处理器，由于寄存器宽度⼤
//于⼀个字节，那么必然存在着⼀个如何将多个字节安排的问题。因此就导致了⼤端存储模式和⼩端存
//储模式。


//2.3 练习
//2.3.1 练习1
//请简述⼤端字节序和⼩端字节序的概念，设计⼀个⼩程序来判断当前机器的字节序。（10分）-百度笔试题
//int main()
//{
//	int n = 1;
//	if (*(char*)&n)
//		printf("小端");
//	else
//		printf("大端");
//	return 0;
//}

//2.3.2 练习2
//int main()
//{
//	//char到底是有符号的char还是无符号的char是取决于编译器的
//	//在vs上: char==signed char
//	char a = -1;
//	//先看-1是如何存放到char里的:
//	//-1是int类型 补码:11111111 11111111 11111111 11111111
//	//存放到char里后发生截断，只留下低位的11111111
//
//	signed char b = -1;
//	//同上 11111111
//
//	unsigned char c = -1;
//	//同上 11111111
//
//	printf("a=%d,b=%d,c=%d", a, b, c);
//	//%d是打印有符号的整数的，因为a，b，c都是char类型，所以打印的时候要发生整型提升:
//	//对a和b进行整型提升(都是有符号的,高位补符号位)都是:
//	//11111111 11111111 11111111 11111111(补码)(整型提升要看类型，有符号的高位补符号位，无符号高位补0)
//	//最后打印原码:10000000 00000000 00000000 00000001
//
//	//对c整型提升
//	//由于c是无符号的，所以高位补0
//	//00000000 00000000 00000000 11111111(补码)
//	//由于最高位是0，被认为是正数，正数的原码反码补码相同，就直接打印
//	return 0;
//}


//2.3.3 练习3
//int main()
//{
//	char a = -128;
//	//-128原码:1000000 00000000 00000000 10000000
//	//补码:11111111 11111111 11111111 1000000
//	//截断存放到a中: 10000000
//	printf("%u\n", a);
//	//%u打印无符号的整数，所有位都是数值位
//	//a发生整型提升:11111111 11111111 11111111 1000000
//	//由于%u打印正数，因此这个数被认为是个正数，不需要转到原码，直接打印这个数,是个非常大的数
//	return 0;
//}

//int main()
//{
//	char a = 128;
//	printf("%u\n", a);//同上
//	return 0;
//}

//2.3.4 练习4
//int main()
//{
//	char a[1000];
//	int i;
//	for (i = 0; i < 1000; i++)
//	{
//		a[i] = -1 - i;
//	}
//	//-1 -2 -3 .... -126 -127 -128 127 126 .... 3 2 1 0
//	printf("%d", strlen(a));
//	return 0;
//}